Project 1 - 自我介紹程式(5):衍伸變化並複習前幾天的內容 - 實作多語系與跨視窗應用
https://ithelp.ithome.com.tw/articles/10214008
1.多語系檔案 - 專案是每個程式作品的單位:
2.選擇語系 - 讓程式在對的時間點完成一件事:
讀寫檔跟多語系一樣,是許多資訊系統當中常見的功能需求。然而實作讀寫檔的過程非常容易造成程式的錯誤。包括資料夾或檔案不存在、資料為Null或空值、型態的錯誤。面對這些錯誤對學習者和教學者造成無數的挫折感和時間的耗費
當我輸入個人資訊、選擇大頭貼,按下保存之後,將這些資訊存到檔案當中:
存檔之後,在程式的檔案附近看到被保存的JSON資料和圖片檔:
個人資料在JSON的資料格式:
可以實作的檔案類型包括「TXT、CSV、JSON、XML、XLS」,每一種檔案格式都有對應的IO函式庫。今天我們將使用Json.net函式庫,以JSON來實作檔案讀寫
在C#,可以使用Image函式庫來處理圖片,或者透過Stream 讀取Byte[]後轉成陣列
讀取檔案結果檔案不存在
讀取檔案資料,結果因為檔案裡面的資料結構有問題,無法存到變數當中
將資料寫入檔案,結果資料夾或檔案路徑不存在
將資料寫入檔案,結果資料為NULL,寫入文字檔的過程中無法轉成文字造成錯誤。寫入圖片檔的過程中無法轉成圖片格式造成錯誤
1.確認資料夾與檔案是否存在
// 如果資校夾不存在
if (!Directory.Exists(dirPath))
{
    
}
// 如果檔案不存在
if (!File.Exists(jsonPath))
{
    
}
(1) 建立資料夾與檔案
// 建立資料夾
Directory.CreateDirectory(dirPath);
// 建立檔案
File.Create(jsonPath).Close();
(2) 讀檔
// 從JSON檔讀取先前保存的個人資訊
    string introductionJsonStr = File.ReadAllText(filePath);
    photoBox.Image = Image.FromFile(imagePath);
(3) 寫檔
    // 保存個人資訊到JSON
    File.WriteAllText(jsonPath, JsonConvert.SerializeObject(introductionJson));
    // 保存個人大頭貼到Jpeg 圖片
    photoBox.Image.Save(imagePath, ImageFormat.Jpeg);
3.進行讀檔後、寫檔前,確認資料結構是否正常
    JObject introductionJson = (JObject)JsonConvert.DeserializeObject(introductionJsonStr);
    //讀完資料發現資料空白、結構有缺漏、結構被破壞,不要讀取
    if (introductionJson == null || 
        !introductionJson.ContainsKey("Name") || !introductionJson.ContainsKey("HomeTown") || !introductionJson.ContainsKey("BirthDate"))
    {
        return;
    }
     else
    {
        nameTextBox.Text = introductionJson["Name"].ToString();
        homeTownTextBox.Text = introductionJson["HomeTown"].ToString();
        birthdate_YearBox.Text = introductionJson["BirthDate"].ToString().Split('-')[0];
        birthdate_MonthBox.Text = introductionJson["BirthDate"].ToString().Split('-')[1];
        birthdate_DayBox.Text = introductionJson["BirthDate"].ToString().Split('-')[2];
    }
1.將Json.net函式庫,透過Nuget安裝到專案當中

2.當自我介紹主畫面開啟時(Load事件),進行讀檔
// IntroductionForm.cs
private void IntroductionForm_Load(object sender, EventArgs e)
{
    loadSaveIntroduction();
    MessageBox.Show(LanguageResources.FormStart);
}
/// <summary>
/// 將自我介紹的資訊顯示到畫面上
/// </summary>
private void loadSaveIntroduction()
{
    string dirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data");
    string filePath = Path.Combine(dirPath, "Introduction.json");
    string imagePath = Path.Combine(dirPath, "Photo.jpeg");
    // 每次讀寫檔之前,檢查路徑的資料夾與檔案是否存在,避免發生路徑不存在的錯誤
    if (!Directory.Exists(dirPath) || !File.Exists(filePath) || !File.Exists(imagePath))
    {
        return;
    }
    // 從JSON檔讀取先前保存的個人資訊
    string introductionJsonStr = File.ReadAllText(filePath);
    JObject introductionJson = (JObject)JsonConvert.DeserializeObject(introductionJsonStr);
    //讀完資料發現資料空白、結構有缺漏、結構被破壞,不要讀取
    if (introductionJson == null || 
        !introductionJson.ContainsKey("Name") || !introductionJson.ContainsKey("HomeTown") || !introductionJson.ContainsKey("BirthDate"))
    {
        return;
    }
    else
    {
        nameTextBox.Text = introductionJson["Name"].ToString();
        homeTownTextBox.Text = introductionJson["HomeTown"].ToString();
        birthdate_YearBox.Text = introductionJson["BirthDate"].ToString().Split('-')[0];
        birthdate_MonthBox.Text = introductionJson["BirthDate"].ToString().Split('-')[1];
        birthdate_DayBox.Text = introductionJson["BirthDate"].ToString().Split('-')[2];
    }
    photoBox.Image = Image.FromFile(imagePath);
}
3.當使用者按下,「保存個人資訊」時,將資料寫入JSON與圖片檔
// IntroductionForm.cs
/// <summary>
/// 按下「保存個人資訊」
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void saveBtn_Click(object sender, EventArgs e)
{
    // 校驗每個欄位是否輸入
    TextBox[] allTextBox = new TextBox[] { nameTextBox, homeTownTextBox, birthdate_YearBox, birthdate_MonthBox, birthdate_DayBox };
    string[] allTextBoxName = new string[]{LanguageResources.Name, LanguageResources.HomeTown,
                                           LanguageResources.Birthday_Year, LanguageResources.Birthday_Month, LanguageResources.Birthday_Day };
    StringBuilder errorMsg = new StringBuilder();
    for (int i = 0; i < allTextBox.GetLength(0); i++)
    {
        if (string.IsNullOrEmpty(allTextBox[i].Text))
        {
            errorMsg.AppendLine(string.Format(LanguageResources.Message_PleaseInput, allTextBoxName[i]));
        }
    }
    if (errorMsg.ToString() != "")
    {
        MessageBox.Show(errorMsg.ToString(), "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    // 校驗日期格式是否正確
    if (!Regex.IsMatch(birthdate_YearBox.Text, @"\d") || !Regex.IsMatch(birthdate_MonthBox.Text, @"\d") || !Regex.IsMatch(birthdate_DayBox.Text, @"\d"))
    {
        MessageBox.Show(LanguageResources.Message_BirthdayNeedNum, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    // 檢查有沒有選擇大頭照
    if (photoBox.Image == null)
    {
        MessageBox.Show(LanguageResources.Message_NoImage, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    string name = nameTextBox.Text;
    string homeTown = homeTownTextBox.Text;
    int birthDate_Year = int.Parse(birthdate_YearBox.Text);
    int birthDate_Month = int.Parse(birthdate_MonthBox.Text);
    int birthDate_Day = int.Parse(birthdate_DayBox.Text);
    JObject introductionJson = new JObject();
    introductionJson.Add("Name", name);
    introductionJson.Add("HomeTown", homeTown);
    introductionJson.Add("BirthDate", string.Format("{0}-{1}-{2}", birthDate_Year, birthDate_Month, birthDate_Day));
    string dirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data");
    string jsonPath = Path.Combine(dirPath, "Introduction.json");
    string imagePath = Path.Combine(dirPath, "Photo.jpeg");
    // 每次讀寫檔之前,檢查路徑的資料夾與檔案是否存在,避免發生路徑不存在的錯誤
    if (!Directory.Exists(dirPath))
    {
        Directory.CreateDirectory(dirPath);
    }
    // 檔案不存在,產生個人資訊的檔案
    if (!File.Exists(jsonPath))
    {
        File.Create(jsonPath).Close();
    }
    // 保存個人資訊到JSON
    File.WriteAllText(jsonPath, JsonConvert.SerializeObject(introductionJson));
    // 保存個人大頭貼到Jpeg 圖片
    photoBox.Image.Save(imagePath, ImageFormat.Jpeg);
    MessageBox.Show("保存完成!", "訊息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
using System;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace IT_Day01
{
    public partial class IntroductionForm : Form
    {
        public IntroductionForm(int languageIndex)
        {
            string[] language = { "zh-TW", "en-US" };
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(language[languageIndex]);
            InitializeComponent();
        }
        /// <summary>
        /// 顯示邀請自我介紹的文字
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IntroductionForm_Load(object sender, EventArgs e)
        {
            loadSaveIntroduction();
            
            MessageBox.Show(LanguageResources.FormStart);
        }
        
        /// <summary>
        /// 將自我介紹的資訊顯示到畫面上
        /// </summary>
        private void loadSaveIntroduction()
        {
            string dirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data");
            string filePath = Path.Combine(dirPath, "Introduction.json");
            string imagePath = Path.Combine(dirPath, "Photo.jpeg");
            // 每次讀寫檔之前,檢查路徑的資料夾與檔案是否存在,避免發生路徑不存在的錯誤
            if (!Directory.Exists(dirPath) || !File.Exists(filePath) || !File.Exists(imagePath))
            {
                return;
            }
            // 從JSON檔讀取先前保存的個人資訊
            string introductionJsonStr = File.ReadAllText(filePath);
            JObject introductionJson = (JObject)JsonConvert.DeserializeObject(introductionJsonStr);
            //讀完資料發現資料空白、結構有缺漏、結構被破壞,不要讀取
            if (introductionJson == null || 
                !introductionJson.ContainsKey("Name") || !introductionJson.ContainsKey("HomeTown") || !introductionJson.ContainsKey("BirthDate"))
            {
                return;
            }
            else
            {
                nameTextBox.Text = introductionJson["Name"].ToString();
                homeTownTextBox.Text = introductionJson["HomeTown"].ToString();
                birthdate_YearBox.Text = introductionJson["BirthDate"].ToString().Split('-')[0];
                birthdate_MonthBox.Text = introductionJson["BirthDate"].ToString().Split('-')[1];
                birthdate_DayBox.Text = introductionJson["BirthDate"].ToString().Split('-')[2];
            }
            photoBox.Image = Image.FromFile(imagePath);
        }
        /// <summary>
        /// 按下「保存個人資訊」
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveBtn_Click(object sender, EventArgs e)
        {
            // 校驗每個欄位是否輸入
            TextBox[] allTextBox = new TextBox[] { nameTextBox, homeTownTextBox, birthdate_YearBox, birthdate_MonthBox, birthdate_DayBox };
            string[] allTextBoxName = new string[]{LanguageResources.Name, LanguageResources.HomeTown,
                                                   LanguageResources.Birthday_Year, LanguageResources.Birthday_Month, LanguageResources.Birthday_Day };
            StringBuilder errorMsg = new StringBuilder();
            for (int i = 0; i < allTextBox.GetLength(0); i++)
            {
                if (string.IsNullOrEmpty(allTextBox[i].Text))
                {
                    errorMsg.AppendLine(string.Format(LanguageResources.Message_PleaseInput, allTextBoxName[i]));
                }
            }
            if (errorMsg.ToString() != "")
            {
                MessageBox.Show(errorMsg.ToString(), "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            // 校驗日期格式是否正確
            if (!Regex.IsMatch(birthdate_YearBox.Text, @"\d") || !Regex.IsMatch(birthdate_MonthBox.Text, @"\d") || !Regex.IsMatch(birthdate_DayBox.Text, @"\d"))
            {
                MessageBox.Show(LanguageResources.Message_BirthdayNeedNum, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            // 檢查有沒有選擇大頭照
            if (photoBox.Image == null)
            {
                MessageBox.Show(LanguageResources.Message_NoImage, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            string name = nameTextBox.Text;
            string homeTown = homeTownTextBox.Text;
            int birthDate_Year = int.Parse(birthdate_YearBox.Text);
            int birthDate_Month = int.Parse(birthdate_MonthBox.Text);
            int birthDate_Day = int.Parse(birthdate_DayBox.Text);
            JObject introductionJson = new JObject();
            introductionJson.Add("Name", name);
            introductionJson.Add("HomeTown", homeTown);
            introductionJson.Add("BirthDate", string.Format("{0}-{1}-{2}", birthDate_Year, birthDate_Month, birthDate_Day));
            string dirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data");
            string jsonPath = Path.Combine(dirPath, "Introduction.json");
            string imagePath = Path.Combine(dirPath, "Photo.jpeg");
            // 每次讀寫檔之前,檢查路徑的資料夾與檔案是否存在,避免發生路徑不存在的錯誤
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            // 檔案不存在,產生個人資訊的檔案
            if (!File.Exists(jsonPath))
            {
                File.Create(jsonPath).Close();
            }
            // 保存個人資訊到JSON
            File.WriteAllText(jsonPath, JsonConvert.SerializeObject(introductionJson));
            // 保存個人大頭貼到Jpeg 圖片
            photoBox.Image.Save(imagePath, ImageFormat.Jpeg);
            MessageBox.Show("保存完成!", "訊息", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        
        /// <summary>
        /// 選擇相片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void photoBox_DoubleClick(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png) | *.jpg; *.jpeg; *.jpe; *.jfif; *.png";
            if (fileDialog.ShowDialog() == DialogResult.OK && fileDialog.FileName != "")
            {
                photoBox.Image = Image.FromFile(fileDialog.FileName);
            }
        }
        /// <summary>
        /// 自我介紹按鈕滑鼠偵聽事件:處理按下自我介紹按鈕後要做的事情
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showIntroductionBtn_Click(object sender, EventArgs e)
        {
            // 校驗每個欄位是否輸入
            TextBox[] allTextBox = new TextBox[] { nameTextBox, homeTownTextBox, birthdate_YearBox, birthdate_MonthBox, birthdate_DayBox };
            string[] allTextBoxName = new string[]{LanguageResources.Name, LanguageResources.HomeTown,
                                                   LanguageResources.Birthday_Year, LanguageResources.Birthday_Month, LanguageResources.Birthday_Day };
            StringBuilder errorMsg = new StringBuilder();
            for (int i = 0; i < allTextBox.GetLength(0); i++)
            {
                if (string.IsNullOrEmpty(allTextBox[i].Text))
                {
                    errorMsg.AppendLine(string.Format(LanguageResources.Message_PleaseInput, allTextBoxName[i]));
                }
            }
            if (errorMsg.ToString() != "")
            {
                MessageBox.Show(errorMsg.ToString());
                return;
            }
            // 校驗日期格式是否正確
            if (!Regex.IsMatch(birthdate_YearBox.Text, @"\d") || !Regex.IsMatch(birthdate_MonthBox.Text, @"\d") || !Regex.IsMatch(birthdate_DayBox.Text, @"\d"))
            {
                MessageBox.Show(LanguageResources.Message_BirthdayNeedNum);
                return;
            }
            // 取得使用者輸入的姓名和家鄉
            string name = nameTextBox.Text;
            string homeTown = homeTownTextBox.Text;
            // 取得當下的日期
            int today_Year = DateTime.Today.Year;
            int today_Month = DateTime.Today.Month;
            int today_Day = DateTime.Today.Day;
            int yearOld;
            // 計算年齡
            int birthDate_Year = int.Parse(birthdate_YearBox.Text);
            int birthDate_Month = int.Parse(birthdate_MonthBox.Text);
            int birthDate_Day = int.Parse(birthdate_DayBox.Text);
            yearOld = today_Year - int.Parse(birthdate_YearBox.Text);
            if (today_Month < birthDate_Month || (today_Month == birthDate_Month && today_Day < birthDate_Day))
            {
                yearOld = yearOld - 1;
            }
            // 顯示自我介紹
            string introductionText = string.Format(LanguageResources.Message_IntrouductionText, name, homeTown, yearOld);
            MessageBox.Show(introductionText);
        }
        /// <summary>
        /// 自訂函式:判斷是否為整數
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        private bool stringIsInt(string num)
        {
            int tryParse;
            return int.TryParse(num, out tryParse);
        }
        /// <summary>
        /// 生日欄位的鍵盤偵聽事件,讓使用者只能輸入數字
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void birthdate_YearBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            char inputChar = e.KeyChar;
            e.Handled = ! (char.IsDigit(inputChar) || char.IsControl(e.KeyChar));
        }
    }
}
讀寫檔看似是一個簡單的輸入輸出流程,往往需要考量到檔案和型態在使用者的操作過程,可能因為什麼狀況而造成程式錯誤。建議自己在不看程式碼的情況下練習讀寫檔的功能,對於型態和檔案讀寫的經驗和能力也會幫助許多。
自我介紹的程式功能大致完成了,但是你從程式結構面的角度會發現,程式碼全部混在同一個檔案,也有許多重複的地方。明天為我們的程式碼結構進行整理,準備為第一支自我介紹程式畫下句點。